/* ****************************************************************
Haxe RTMP Shared Object Chat

This chat illustrates one method to use shared objects in a chat
room. Some of the code is specifically written to illustrate
certain features of Remote Shared Objects.

The input text is sent to the server in the line
soText.setProperty("last",[nick,tf.text]);
which sends an array. In the function onTextSync, that array
has been returned from the server, but is now an object, so in
order to recover the fields,
var n = Reflect.field(o,"0");
Where "0" refers to the offset in the original array.

Also, if this chat gets busy enough, the occasional update sent
to the server will be rejected because the serial number of the
object in the flash player may be lagging behind. It would be
proper practice to capture the last input text, disable the
input field, then wait for a onTextSync success. If onTextSync
returned an error, the last input text can simply be resent
to the server. This is a normal condition with shared objects.
** ****************************************************************/
package samples.chat;

import flash.net.NetConnection;
import flash.net.SharedObject;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;

class ChatClient {
	static var persistent 	: Bool = false;
	static var host 	: String;
	static var nick 	: String;
	static var tf 		: flash.text.TextField;
	static var log 	: flash.text.TextField;
	static var nc		: NetConnection;
	static var soNicks	: SharedObject;
	static var soText	: SharedObject;

	static function initDisplay() {
		var mc = flash.Lib.current;
		// create an input textfield
		tf = new flash.text.TextField();
		tf.defaultTextFormat = getTextFormat();
		tf.x = 5;
		tf.y = mc.stage.stageHeight - 25;
		tf.width = mc.stage.stageWidth - 10;
		tf.height = 20;
		tf.type = "input";
		tf.border = true;
		tf.background = true;
		tf.backgroundColor = 0xEEEEEE;
		mc.addChild(tf);

		// create a chat log
		log = new flash.text.TextField();
		log.defaultTextFormat = getTextFormat();
		log.x = 5;
		log.y = 5;
		log.width = mc.stage.stageWidth - 10;
		log.height = mc.stage.stageHeight - 35;
		log.background = true;
		log.backgroundColor = 0xFFFFFF;
		log.wordWrap = true;
		//log.html = true;
		log.border = true;
		log.multiline = true;
		mc.addChild(log);

		mc.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
	}

	static function onKeyUp(e:flash.events.KeyboardEvent) {
		// ENTER pressed ?
		if( e.keyCode == 13 ) {
			send(tf.text);
			tf.text = "";
		}
	}

	static function send( text : String ) {
		if( nick == null ) {
			nick = text;
			soNicks.setProperty("join",tf.text);
			return;
		}
		soText.setProperty("last",[nick,tf.text]);
	}

	public static function userJoin( name ) {
		display("<b>"+name+"</b> has joined");
	}

	public static function userLeave( name ) {
		display("User leave <b>"+name+"</b>");
	}

	public static function userSay( name : String, text : String ) {
		display("<b>"+name+ " :</b> "+text.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;"));
	}

	static function display( line : String ) {
		var bottom = (log.scrollV == log.maxScrollV);
		log.htmlText += line + "<br>";
		if( bottom )
			log.scrollV = log.maxScrollV;
	}

	static public function getTextFormat() {
		var fmt = new flash.text.TextFormat();
		fmt.font = "Times New Roman";
		fmt.color = 0x000000;
		fmt.size = 12;
		fmt.underline = false;
		return fmt;
	}

	static function doConnect() {
		soNicks = SharedObject.getRemote("nicks", nc.uri, persistent,false);
		soNicks.addEventListener(flash.events.SyncEvent.SYNC, onNickSync);
		soNicks.addEventListener(flash.events.AsyncErrorEvent.ASYNC_ERROR, onSyncError);
		soNicks.addEventListener(flash.events.NetStatusEvent.NET_STATUS,onSONetStatusEvent);
		soNicks.connect(nc);

		soText = SharedObject.getRemote("text", nc.uri, persistent,false);
		soText.addEventListener(flash.events.SyncEvent.SYNC, onTextSync);
		soText.addEventListener(flash.events.AsyncErrorEvent.ASYNC_ERROR, onSyncError);
		soText.addEventListener(flash.events.NetStatusEvent.NET_STATUS,onSONetStatusEvent);
		soText.connect(nc);
	}

	public static function onNetStatusEvent(e : Dynamic) : Void {
		if( e.info.code == "NetConnection.Connect.Success" ) {
			display("Connected");
			doConnect();
			display("Please enter your name in the bottom textfield to login and press ENTER");
		}
	}

	public static function doTrace( v : Dynamic, ?pos : haxe.PosInfos ) {
		var text = pos.fileName+"("+pos.lineNumber+") : "+Std.string(v);
		display(text);
	}

	static function onSONetStatusEvent(e:flash.events.NetStatusEvent) {
		doTrace(here.methodName + " " + e);
	}

	static function onTextSync(e:flash.events.SyncEvent) {
		for(i in e.changeList) {
			//for(f in Reflect.fields(i)) {
			//			doTrace("Field: " + f + " " + Reflect.field(i,f));
			//}
			if(untyped i.code == "change") {
				var key = Std.string(untyped i.name);
				var val = Std.string(untyped i.oldValue);
				if(key == "last") {
					if(Reflect.field(soText.data,key) != null) {
						var o = Reflect.field(soText.data,key);
						var n = Reflect.field(o,"0");
						var t = Reflect.field(o,"1");
						userSay(n,t);
					}
				}
			}
			else if(untyped i.code == "reject") {
				// This is where you would replay the last text message
				display("*** Text rejected by server ***");
			}
			else if(untyped i.code == "success") {
				var key = Std.string(untyped i.name);
				if(key == "last") {
					var o = Reflect.field(soText.data,key);
					userSay(nick, Reflect.field(o,"1"));
				}
			}
		}
	}

	static function onNickSync(e:flash.events.SyncEvent) {
		for(i in e.changeList) {
			if(untyped i.code == "change") {
				var key = Std.string(untyped i.name);
				var val = Std.string(untyped i.oldValue);
				userJoin(Reflect.field(soNicks.data,key));
			}
			else if(untyped i.code == "reject") {
				display("*** Nickname rejected by server ***");
			}
			else if(untyped i.code == "success") {
			}
		}
	}

	static function onSyncError(e:flash.events.AsyncErrorEvent) {
		doTrace(here.methodName + " " + e);
	}

	static function main() {
		var o = flash.Lib.current.loaderInfo.parameters;
		flash.net.NetConnection.defaultObjectEncoding = flash.net.ObjectEncoding.AMF0;
		initDisplay();

		haxe.Log.trace = doTrace;
		host = "rtmp://"+flash.Lib.current.loaderInfo.parameters.host;
		display("Connecting to server");
		nc = new NetConnection();
		nc.addEventListener(flash.events.NetStatusEvent.NET_STATUS,onNetStatusEvent);
		nc.connect(host+"/SOSample");
	}
}
